/**
 * @addtogroup FXCRT
 * @{
 */

/**
 * @file
 * @brief Exception.
 */

#ifndef _FXM_OPENSOURCE_

#ifndef _FX_EXCEPTION_H_
#define _FX_EXCEPTION_H_

#define FX_SETJMP(BUF)			setjmp(BUF)
#define FX_LONGJMP(BUF, VAL)	longjmp(BUF, VAL)
#define FX_JMP_BUF				jmp_buf

// jmp depth default 512.
#define FX_JMP_DEPTH			512

#ifndef _FX_ERROR_H_
#include "fx_error.h"
#endif

#ifndef _FX_SYSTEM_H_
#include "fx_system.h"
#endif

#ifndef _FX_BASIC_H_
#include "fx_basic.h"
#endif


class CFX_Exception;
class CFX_JmpStack;

/** 
 * Jump stack class.
 */
class CFX_JmpStack : public CFX_Object
{
public:
	CFX_JmpStack() : code(0) {}

	int code;
	FX_JMP_BUF buffer;
};

class CFX_Exception : public CFX_Object
{
public:
	CFX_Exception();
	~CFX_Exception();
	
	FX_BOOL Catch(int value);
	FX_BOOL CatchAll();
	void	EndTry();

protected:
	FX_BOOL	m_bCatched;
};

#ifdef _FX_SUPPORT_EXCEPTION_
//*****************************************************************************
//* Exception macro.
//*****************************************************************************
/**
 * @brief Exception macro.
 * 
 * @note IMPORTANT: Between FX_TRY and FX_ALWARYS or FX_CATCH or FX_CATCHALL must't use the return or goto or longjmp instructor.
 *
 *       usage: 
 *            FX_TRY {
 *                   ...
 *            } FX_ALWAYS() {
 *                   ...
 *            } [ FX_CATCHALL() | FX_CATCH(1) ] {
 *                   ...
 *            } FX_ENDTYR
 */
/*@{*/

#define FX_TRY \
	{ \
	CFX_Exception __exception; \
	if (FX_Push_Try(FX_Thread_GetExceptionContext()->m_pErrContext) && (FX_Thread_GetExceptionContext()->m_pErrContext->stack[FX_Thread_GetExceptionContext()->m_pErrContext->top].code = \
		FX_SETJMP(FX_Thread_GetExceptionContext()->m_pErrContext->stack[FX_Thread_GetExceptionContext()->m_pErrContext->top].buffer)) == 0)

#define FX_ALWAYS() \
		if (1)

#define FX_CATCH(value) \
		if (__exception.Catch(value))

#define FX_CATCHALL() \
		if (__exception.CatchAll())

#define FX_ENDTRY \
		__exception.EndTry(); \
	}

//This will throw before throw error value.
#define FX_CONTINUETHROW FX_ContinueThrow()

//This will get current catch error value.
#define FX_GETCATCHCODE FX_GetCatchCode()

/*@}*/

/**
 * Throw an exception with a error code.
 *
 * @param[in] iErrCode	The error code.
 */
void FX_Throw(int iErrCode);
void FX_ContinueThrow();

/**
 * Get current catch error code.
 *
 */
int FX_GetCatchCode();

#endif //_FX_SUPPORT_EXCEPTION_

//*****************************************************************************
//* Exception class.
//*****************************************************************************
class CFX_ExceptionErrorContext : public CFX_Object
{
public:
	CFX_ExceptionErrorContext();

	void	Initialize();
	void	Finalize();

	int top;
	CFX_JmpStack *stack;
};

class CFX_ExceptionContext : public CFX_Object
{
public:
	CFX_ExceptionContext() : m_pErrContext(NULL) {}

	void	Initialize();
	void	Finalize();
	
	CFX_ExceptionErrorContext* m_pErrContext;
};

//*****************************************************************************
//* The following function will be implicit called by previous macros. 
//* Please don't direct use.
//*****************************************************************************
FX_BOOL FX_Push_Try(CFX_ExceptionErrorContext* pErrContext);

#endif //_FX_EXCEPTION_H_

#endif //_FXM_OPENSOURCE_

/** @} */
